﻿using OkTool.Regex;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace OkTool.Util
{
    /// <summary>
    /// 身份证工具类
    /// </summary>
    public class IdcardUtil
    {

        /// <summary>
        /// 中国公民身份证号码最小长度。
        /// </summary>
        private static  int CHINA_ID_MIN_LENGTH = 15;

        /// <summary>
        /// 中国公民身份证号码最大长度。
        /// </summary>
        private static  int CHINA_ID_MAX_LENGTH = 18;

        /// <summary>
        /// 每位加权因子
        /// </summary>
        private static int[] POWER = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

        /// <summary>
        /// 省市代码表
        /// </summary>
        private static  Dictionary<string, string> CITY_CODES = new Dictionary<string,string>();

        /// <summary>
        /// 台湾身份首字母对应数字
        /// </summary>
        private static Dictionary<char, int> TW_FIRST_CODE = new Dictionary<char, int>();

        static IdcardUtil()
        {
            CITY_CODES.Add("11", "北京");
            CITY_CODES.Add("12", "天津");
            CITY_CODES.Add("13", "河北");
            CITY_CODES.Add("14", "山西");
            CITY_CODES.Add("15", "内蒙古");
            CITY_CODES.Add("21", "辽宁");
            CITY_CODES.Add("22", "吉林");
            CITY_CODES.Add("23", "黑龙江");
            CITY_CODES.Add("31", "上海");
            CITY_CODES.Add("32", "江苏");
            CITY_CODES.Add("33", "浙江");
            CITY_CODES.Add("34", "安徽");
            CITY_CODES.Add("35", "福建");
            CITY_CODES.Add("36", "江西");
            CITY_CODES.Add("37", "山东");
            CITY_CODES.Add("41", "河南");
            CITY_CODES.Add("42", "湖北");
            CITY_CODES.Add("43", "湖南");
            CITY_CODES.Add("44", "广东");
            CITY_CODES.Add("45", "广西");
            CITY_CODES.Add("46", "海南");
            CITY_CODES.Add("50", "重庆");
            CITY_CODES.Add("51", "四川");
            CITY_CODES.Add("52", "贵州");
            CITY_CODES.Add("53", "云南");
            CITY_CODES.Add("54", "西藏");
            CITY_CODES.Add("61", "陕西");
            CITY_CODES.Add("62", "甘肃");
            CITY_CODES.Add("63", "青海");
            CITY_CODES.Add("64", "宁夏");
            CITY_CODES.Add("65", "新疆");
            CITY_CODES.Add("71", "台湾");
            CITY_CODES.Add("81", "香港");
            CITY_CODES.Add("82", "澳门");
            //issue#1277，台湾身份证号码以83开头，但是行政区划为71
            CITY_CODES.Add("83", "台湾");
            CITY_CODES.Add("91", "国外");

            TW_FIRST_CODE.Add('A', 10);
            TW_FIRST_CODE.Add('B', 11);
            TW_FIRST_CODE.Add('C', 12);
            TW_FIRST_CODE.Add('D', 13);
            TW_FIRST_CODE.Add('E', 14);
            TW_FIRST_CODE.Add('F', 15);
            TW_FIRST_CODE.Add('G', 16);
            TW_FIRST_CODE.Add('H', 17);
            TW_FIRST_CODE.Add('J', 18);
            TW_FIRST_CODE.Add('K', 19);
            TW_FIRST_CODE.Add('L', 20);
            TW_FIRST_CODE.Add('M', 21);
            TW_FIRST_CODE.Add('N', 22);
            TW_FIRST_CODE.Add('P', 23);
            TW_FIRST_CODE.Add('Q', 24);
            TW_FIRST_CODE.Add('R', 25);
            TW_FIRST_CODE.Add('S', 26);
            TW_FIRST_CODE.Add('T', 27);
            TW_FIRST_CODE.Add('U', 28);
            TW_FIRST_CODE.Add('V', 29);
            TW_FIRST_CODE.Add('X', 30);
            TW_FIRST_CODE.Add('Y', 31);
            TW_FIRST_CODE.Add('W', 32);
            TW_FIRST_CODE.Add('Z', 33);
            TW_FIRST_CODE.Add('I', 34);
            TW_FIRST_CODE.Add('O', 35);
        }

        /// <summary>
        /// 将15位身份证号码转换为18位
        /// </summary>
        /// <param name="idCard">15位身份证号码</param>
        /// <returns></returns>
        public static string Convert15To18(string idCard)
        {
            StringBuilder idCard18;
            if (idCard.Length != CHINA_ID_MIN_LENGTH)
            {
                return null;
            }
            if (RegexUtil.IsMatch(idCard,RegexPool.NUMBERS))
            {
                // 获取出生年月日
                String birthday = StrUtil.Sub(idCard, 6, 12);
                DateTime birthDate = DateUtil.Parse(birthday, "yyMMdd");
                // 获取出生年(完全表现形式,如：2010)
                int sYear = DateUtil.Year(birthDate);
                if (sYear > 2000)
                {
                    // 2000年之后不存在15位身份证号，此处用于修复此问题的判断
                    sYear -= 100;
                }
                idCard18 = StrUtil.Builder().Append(idCard, 0, 6).Append(sYear).Append(idCard.Substring(8));
                // 获取校验位
                char sVal = GetCheckCode18(idCard18.ToString());
                idCard18.Append(sVal);
            }
            else
            {
                return null;
            }
            return idCard18.ToString();
        }
        /// <summary>
        /// 将18位身份证号码转换为15位
        /// </summary>
        /// <param name="idCard"></param>
        /// <returns></returns>
        public static string Convert18To15(string idCard)
        {
            if (StrUtil.IsNotBlank(idCard) && IsValidCard18(idCard))
            {
                return StrUtil.Sub(idCard,0, 6) + StrUtil.Sub(idCard,8, idCard.Length - 1);
            }
            return idCard;
        }

        /// <summary>
        /// 判断18位身份证的合法性
        /// </summary>
        /// <param name="idcard"></param>
        /// <returns></returns>
        public static bool IsValidCard18(String idcard)
        {
            return IsValidCard18(idcard, true);
        }
        /// <summary>
        /// 判断18位身份证的合法性
        /// </summary>
        /// <param name="idcard"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        public static bool IsValidCard18(String idcard, bool ignoreCase)
        {
            if (idcard == null)
            {
                return false;
            }
            if (CHINA_ID_MAX_LENGTH != idcard.Length)
            {
                return false;
            }

            // 省份
            string proCode = idcard.Substring(0, 2);
            if (!CITY_CODES.ContainsKey(proCode))
            {
                return false;
            }

            //校验生日
            if (false == ValidatorUtil.IsBirthday(StrUtil.Sub( idcard,6, 14)))
            {
                return false;
            }

            // 前17位
            string code17 =StrUtil.Sub( idcard,0, 17);
            if (RegexUtil.IsMatch(code17,RegexPool.NUMBERS))
            {
                // 获取校验位
                char val = GetCheckCode18(code17);
                // 第18位
                if (ignoreCase)
                {
                    return char.ToLower(val) == char.ToLower(idcard[17]);
                }
                return val == idcard[17];
            }
            return false;
        }

        /// <summary>
        /// 根据身份编号获取生日，只支持15或18位身份证号码
        /// </summary>
        /// <param name="idCard"></param>
        /// <returns>生日(yyyyMMdd)</returns>
        public static string GetBirth(string idCard)
        {
            if (StrUtil.IsBlank(idCard))
            {
                return null;
            }
            int len = idCard.Length;
            if (len < CHINA_ID_MIN_LENGTH)
            {
                return null;
            }
            else if (len == CHINA_ID_MIN_LENGTH)
            {
                idCard = Convert15To18(idCard);
            }
            return StrUtil.Sub(idCard,6, 14);
        }

        /// <summary>
        /// 从身份证号码中获取生日日期，只支持15或18位身份证号码
        /// </summary>
        /// <param name="idcard"></param>
        /// <returns></returns>
        public static String GetBirthByIdCard(String idcard)
        {
            return GetBirth(idcard);
        }

        /// <summary>
        /// 从身份证号码中获取生日日期，只支持15或18位身份证号码
        /// </summary>
        /// <param name="idCard"></param>
        /// <returns></returns>
        public static DateTime? GetBirthDate(String idCard)
        {
             String birthByIdCard = GetBirthByIdCard(idCard);
            return null == birthByIdCard ? null : DateUtil.Parse(birthByIdCard, OkTool.Date.DatePattern.PURE_DATE_PATTERN);
        }

        /// <summary>
        /// 将身份证的每位和对应位的加权因子相乘之后，再得到和值
        /// </summary>
        /// <param name="iArr">身份证号码的数组</param>
        /// <returns>身份证编码</returns>
        private static int GetPowerSum(char[] iArr)
        {
            int iSum = 0;
            if (POWER.Length == iArr.Length)
            {
                for (int i = 0; i < iArr.Length; i++)
                {
                    iSum += int.Parse((iArr[i]).ToString()) * POWER[i];
                }
            }
            return iSum;
        }

        /// <summary>
        /// 获得18位身份证校验码
        /// </summary>
        /// <param name="code17">18位身份证号中的前17位</param>
        /// <returns>第18位</returns>
        private static char GetCheckCode18(string code17)
        {
            int sum = GetPowerSum(code17.ToCharArray());
            return GetCheckCode18(sum);
        }


        /// <summary>
        /// 将power和值与11取模获得余数进行校验码判断
        /// </summary>
        /// <param name="iSum"></param>
        /// <returns></returns>
        private static char GetCheckCode18(int iSum)
        {
            switch (iSum % 11)
            {
                case 10:
                    return '2';
                case 9:
                    return '3';
                case 8:
                    return '4';
                case 7:
                    return '5';
                case 6:
                    return '6';
                case 5:
                    return '7';
                case 4:
                    return '8';
                case 3:
                    return '9';
                case 2:
                    return 'X';
                case 1:
                    return '0';
                case 0:
                    return '1';
                default:
                    return Text.StrPool.C_SPACE;
            }
        }

    }
}
